package com.journey.flower.core.util;

import com.journey.flower.core.code.TwoDimensionCode;
import lombok.Data;
import org.springframework.core.io.ClassPathResource;

import javax.print.*;
import javax.print.attribute.standard.PrinterName;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;

/**
 * @author JourneyOfFlower
 * @version v1.0
 * @date 2022.09.23 11:23
 * @description ZB_TODO
 */
@Data
public class ZplPrinter {
    private String printerURI = null;// 打印机完整路径
    private PrintService printService = null;// 打印机服务
    private byte[] dotFont;
    private String begin = "^XA"; // 标签格式以^XA开始
    private String end = "^XZ"; // 标签格式以^XZ结束
    private String content = "";

    public static void printBarcode() {
        String electronicCode = "123457FFFFEEEB";

        ZplPrinter zplPrinter = new ZplPrinter("ZDesigner ZT410-300dpi ZPL");
        String productName = "product.getName";
        productName = productName.replace("，", ",");
        productName = productName.replace("（", "(");
        productName = productName.replace("）", ")");
        productName = productName.replace("×", "*");
        productName = "product.getName";
        if (productName.length() < 25) {
            zplPrinter.setText(productName, 15, 10, 25, 25, 15, 1, 1, 24);
        } else if (productName.length() > 25) {
            if (productName.length() > 50) {
                productName = productName.substring(0, 50) + "...";
            }
            zplPrinter.setText(productName.substring(0, 25), 15, 10, 25, 25, 15, 1, 1, 24);
            zplPrinter.setText(productName.substring(25, productName.length()), 15, 40, 25, 25, 15, 1, 1, 24);
        }
        String bar0Zpl = "^FO25,80^BY3,3.0,150^BCN,,Y,N,N^FD${data}^FS";//条码样式模板
        zplPrinter.setBarcode(electronicCode, bar0Zpl);
        System.out.println(zplPrinter.getZpl());
        zplPrinter.print(zplPrinter.getZpl());
    }

    public static void printQRCOde() {
        // 打印二维码
        String stockCode = "123457FFFFEEEB";

        ZplPrinter codeZplPrinter = new ZplPrinter("ZDesigner ZT410-300dpi ZPL");
        TwoDimensionCode handler = new TwoDimensionCode();
        BufferedImage bufferedImage = handler.encoderQRCode(stockCode, "png", 9);
        String codeBegin = Image2Zpl.image2Zpl(bufferedImage);

        codeZplPrinter.setBegin(codeZplPrinter.getBegin() + codeBegin);
        codeZplPrinter.setContent(codeZplPrinter.getContent() + "^FO50,70^XG" + Image2Zpl.imgLength + ",1,1^FS\n");

        codeZplPrinter.setChar(stockCode, 250, 135, 22, 22);

//        content += "^PQ2";//打印2张
        codeZplPrinter.print(codeZplPrinter.getZpl());

        codeZplPrinter.print(codeZplPrinter.getZpl());
    }

    public static void main(String[] args) {

//        printBarcode();
//        printQRCOde();
        ZplPrinter p = new ZplPrinter("ZDesigner ZT410-300dpi ZPL");

        /**第一种方法**/

        String codeBegin = "";
        BufferedImage bufferedImage = null;//二维码
        TwoDimensionCode handler = new TwoDimensionCode();
        String encoderContentStr = "样品编号：TX2-18/03/29-003-123456-123\n委托编号：CEPR1-TX2-2018-130-1234567";
        bufferedImage = handler.encoderQRCode(encoderContentStr, "png", 8);
        codeBegin = Image2Zpl.image2Zpl(bufferedImage);

        p.setText("样品标识", 100, 20, 30, 30, 15, 1, 1, 24);

        p.setBegin(p.getBegin() + codeBegin);
        p.setContent(p.getContent() + "^FO50,70^XG" + Image2Zpl.imgLength + ",1,1^FS\n");

        p.setText("样品编号:", 5, 50, 25, 25, 13, 1, 1, 24);
        p.setChar("TX2-18/03/09-0003", 5, 85, 22, 22);
        p.setText("委托编号:", 5, 120, 25, 25, 13, 1, 1, 24);
        p.setChar("CEPR1-TX2-2018-1300", 5, 155, 22, 22);
        p.setText("样品名称:生产制造执行系统", 5, 200, 25, 25, 13, 1, 1, 24);

        p.setText("待检", 5, 288, 30, 30, 15, 1, 1, 24);
        p.setBox(55, 288, 40, 25, 1);
        p.setText("在检", 105, 288, 30, 30, 15, 1, 1, 24);
        p.setBox(155, 288, 40, 25, 1);
        p.setText("已检", 205, 288, 30, 30, 15, 1, 1, 24);
        p.setBox(255, 290, 40, 25, 1);
        p.setText("留样", 305, 288, 30, 30, 15, 1, 1, 24);
        p.setBox(355, 288, 40, 25, 1);

//        content += "^PQ2";//打印2张
        String zpl2 = p.getZpl();
        System.out.println("zpl2=======" + zpl2);


//      第二种方法
        try {
            BufferedImage labelImg = ImageProducerUtil.createImage1();
            zpl2 = Image2Zpl.image2Zpl2(labelImg);
            System.out.println("zpl2=======" + zpl2);
        } catch (Exception e) {
            e.printStackTrace();
        }


        boolean result2 = p.print(zpl2);
    }


    /**
     * 构造方法
     *
     * @param printerURI 打印机路径
     */
    public ZplPrinter(String printerURI) {
        this.printerURI = printerURI;
        // 加载点阵字库 汉字需要
        File file = null;
        try {
            file = new ClassPathResource("lib/ts24.lib").getFile();
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("resources/lib/ts24.lib文件不存在");
        }

        FileInputStream fis;
        try {
            fis = new FileInputStream(file);
            dotFont = new byte[fis.available()];
            fis.read(dotFont);
            fis.close();
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("读取文字文件异常" + e.getMessage());
        }

        // 初始化打印机
        PrintService[] services = PrintServiceLookup.lookupPrintServices(null, null);
        if (services != null && services.length > 0) {
            for (PrintService service : services) {
                if (printerURI.equals(service.getName())) {
                    printService = service;
                    break;
                }
            }
        }
        if (printService == null) {
            System.out.println("没有找到打印机：[" + printerURI + "]");
            // 循环出所有的打印机
            if (services != null && services.length > 0) {
                System.out.println("可用的打印机列表：");
                for (PrintService service : services) {
                    System.out.println("[" + service.getName() + "]");
                }
            }
        } else {
            System.out.println("找到打印机：[" + printerURI + "]");
            System.out.println("打印机名称：[" + printService.getAttribute(PrinterName.class).getValue() + "]");
        }
    }

    /**
     * 设置条形码
     *
     * @param barcode 条码字符
     * @param zpl     条码样式模板
     */
    public void setBarcode(String barcode, String zpl) {
        content += zpl.replace("${data}", barcode);
    }

    /**
     * 中文字符、英文字符(包含数字)混合
     *
     * @param str 中文、英文
     * @param x   x坐标
     * @param y   y坐标
     * @param eh  英文字体高度height
     * @param ew  英文字体宽度width
     * @param es  英文字体间距spacing
     * @param mx  中文x轴字体图形放大倍率。范围1-10，默认1
     * @param my  中文y轴字体图形放大倍率。范围1-10，默认1
     * @param ms  中文字体间距。24是个比较合适的值。
     */
    public void setText(String str, int x, int y, int eh, int ew, int es, int mx, int my, int ms) {
        byte[] ch = str2bytes(str);
        for (int off = 0; off < ch.length; ) {
            if (((int) ch[off] & 0x00ff) >= 0xA0) {//ASCII码值"0xa0"表示汉字的开始
                int qcode = ch[off] & 0xff;
                int wcode = ch[off + 1] & 0xff;
                content += String.format("^FO%d,%d^XG0000%01X%01X,%d,%d^FS\n", x, y, qcode, wcode, mx, my);
                begin += String.format("~DG0000%02X%02X,00072,003,\n", qcode, wcode);
                qcode = (qcode + 128 - 32) & 0x00ff;//区码：或qcode = (qcode - 128 - 32) & 0x00ff;（一般这样用）可达到减去0xA0的效果
                wcode = (wcode + 128 - 32) & 0x00ff;//位码：或wcode = (wcode - 128 - 32) & 0x00ff;（一般这样用）可达到减去0xA0的效果
                int offset = ((int) qcode - 16) * 94 * 72 + ((int) wcode - 1) * 72;//偏移量是指字模首字节距离文件头的相对位置
                for (int j = 0; j < 72; j += 3) {
                    qcode = (int) dotFont[j + offset] & 0x00ff;
                    wcode = (int) dotFont[j + offset + 1] & 0x00ff;
                    int qcode1 = (int) dotFont[j + offset + 2] & 0x00ff;
                    begin += String.format("%02X%02X%02X\n", qcode, wcode, qcode1);//X  --- 以十六进制显示，不足两位则补零
                }
                x = x + ms * mx;
                off = off + 2;//中文包含两个字节
            } else if (((int) ch[off] & 0x00FF) < 0xA0) {
                setChar(String.format("%c", ch[off]), x, y, eh, ew);
                x = x + es;
                off++;//英文包含一个字节
            }
        }
    }

    /**
     * 英文字符串(包含数字)
     *
     * @param str 英文字符串
     * @param x   x坐标
     * @param y   y坐标
     * @param h   高度
     * @param w   宽度
     */
    public void setChar(String str, int x, int y, int h, int w) {
        content += "^FO" + x + "," + y + "^A0," + h + "," + w + "^FD" + str + "^FS";
    }


    /**
     * BQ 二维码 ^BQa,b,c,d,e
     *
     * @param data
     * @param x    x坐标
     * @param y    y坐标
     * @param b    模型 默认值:2(增强) 推荐 其他值:1
     * @param c    放大倍数 默认值:1在150 dpi打印机 2在200 dpi打印机 3在300 dpi打印机 其他值:4至10
     * @param d    校验等级，H Q M L L级：约可纠错7%的数据码字 M级：约可纠错15%的数据码字 Q级：约可纠错25%的数据码字
     *             H级：约可纠错30%的数据码字
     */
    public void setCodeByBQ(String data, int x, int y, int b, int c, String d) {
        content += "^FT" + x + "," + y + "^BQ," + b + "," + c + "," + d + "^FDQA," + data + "^FS";
    }


    /**
     * 打印方框
     * ^FO10,10\n^GB50,760,3,B^FS
     *
     * @param x x坐标
     * @param y y坐标
     * @param w 方框宽度
     * @param h 方框高度
     * @param t 线宽
     */
    public void setBox(int x, int y, int w, int h, int t) {
        content += "^FO" + x + "," + y + "^GB" + w + "," + h + "," + t + "^FS";
    }


    /**
     * 英文字符(包含数字)顺时针旋转90度
     *
     * @param str 英文字符串
     * @param x   x坐标
     * @param y   y坐标
     * @param h   高度
     * @param w   宽度
     */
    public void setCharR(String str, int x, int y, int h, int w) {
        content += "^FO" + x + "," + y + "^A0R," + h + "," + w + "^FD" + str + "^FS";
    }


    /**
     * 获取完整的ZPL
     *
     * @return
     */
    public String getZpl() {
        return begin + content + end;
    }

    /**
     * 重置ZPL指令，当需要打印多张纸的时候需要调用。
     */
    public void resetZpl() {
        begin = "^XA";
        end = "^XZ";
        content = "";
    }

    /**
     * 打印
     *
     * @param zpl 完整的ZPL
     */
    public boolean print(String zpl) {
        if (printService == null) {
            System.out.println("打印出错：没有找到打印机：[" + printerURI + "]");
            return false;
        }
        DocPrintJob job = printService.createPrintJob();
        byte[] by = zpl.getBytes();
        DocFlavor flavor = DocFlavor.BYTE_ARRAY.AUTOSENSE;
        Doc doc = new SimpleDoc(by, flavor, null);
        try {
            job.print(doc, null);
            System.out.println("已打印");
            return true;
        } catch (PrintException e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 字符串转byte[]
     *
     * @param s
     * @return
     */
    private static byte[] str2bytes(String s) {
        if (null == s || "".equals(s)) {
            return null;
        }
        byte[] abytes = null;
        try {
            abytes = s.getBytes("gb2312");
        } catch (UnsupportedEncodingException ex) {
            ex.printStackTrace();
        }
        return abytes;
    }
}
